#include "skey/skey.h"
#include "debug.h"
#define LOG_TAG "[SelfKeyDemo]"

/**
 * @brief ## Switch macro table
 * @author zhengziyang (zhengziyang@zh-jieli.com)
 */

#define KEY_STATE_PRESSED  0
#define KEY_STATE_RELEASED 1

void key_send_message(key_check_member_t* sender,int32_t TriggerSignal)
{
    int msg[2];
    msg[0] = (int)sender;
    msg[1] = TriggerSignal;
    os_taskq_post_type("app_core", MSG_FROM_SELF_KEY, 2, msg); //you need add 'MSG_FROM_SELF_KEY' to app_msg enum
}

static void key_check_handler(void *key_check_list)
{
    key_check_member_t *node;
    uint32_t port_value;
    #define FALL_EDGE 0
    #define RAISE_EDGE 1
    #define NO_EDGE 2
    uint8_t edge;
    keyTriggerTypeDef TriggerResult = KEY_TRIGGER_UNSUPPORT;
    // log_info("%s\n",__FUNCTION__);
    for_each_key_node(node, ((key_check_member_t*)key_check_list)) {
        edge = NO_EDGE;
        port_value = node->getPortValueCallback(node->port_info);
        // log_info("checking...:%d\n",port_value);
        if(node->key_last_state_temp == port_value)
        {
            if((node->key_last_state ^ port_value) != 0)
            {
                edge = port_value;
                // log_info("PORT%c:%d value updated:%d\n",Convert_PORT_num_to_char(node->PORT),Convert_IO_BIT_to_num(node->GPIO_Pin),port_value);
                log_info("edge update: %d\n",edge);
            } //边沿检测
            node->key_last_state = port_value;
        }
        else
        {
            node->key_last_state_temp = port_value;
        }

        // Click Check
        if(edge == RAISE_EDGE)
        {
            node->combo_count ++;
            node->combo_timeout &= 0x0f;
            node->combo_timeout = node->combo_timeout | (node->combo_timeout << 4);
            // log_info("timeout etc:%d,trigger counter:%d\n", node->combo_timeout >> 4,node->combo_count);
        }
        

#define KEY_HOLD_STATE_PRESSED       0x01
#define KEY_HOLD_STATE_RELEASED      0x02
#define KEY_HOLD_STATE_0_TRID        0x04
#define KEY_HOLD_STATE_1_TRID        0x08
#define KEY_HOLD_STATE_2_TRID        0x10
#define KEY_HOLD_STATE_3_TRID        0x20
#define KEY_HOLD_STATE_ANY_LONG_TRID (KEY_HOLD_STATE_0_TRID|KEY_HOLD_STATE_1_TRID|KEY_HOLD_STATE_2_TRID|KEY_HOLD_STATE_3_TRID)

        //Press Check
        if(node->key_last_state == 0)
        {
            node->key_hold_time_counter++;
            node->key_hold_trigger_state &= ~KEY_HOLD_STATE_RELEASED;
            if(((node->key_hold_trigger_state & KEY_HOLD_STATE_PRESSED) == 0) && (node->key_trigger_type & KEY_TRIGGER_PRESSED))
            {
                key_send_message(node,KEY_TRIGGER_PRESSED);
                node->key_hold_trigger_state |= KEY_HOLD_STATE_PRESSED;
            }
#ifndef KEY_CHECK_DISABLE_TIME_HOLD
            if(node->key_hold_time_counter >= node->key_hold_time_check[0])
            {
                if((node->key_trigger_type & KEY_TRIGGER_HOLD_0)&&(!(node->key_hold_trigger_state & KEY_HOLD_STATE_0_TRID)))
                {
                    key_send_message(node, KEY_TRIGGER_HOLD_0 + node->combo_count);
                    node->key_hold_trigger_state |= KEY_HOLD_STATE_0_TRID;
                }
            }
            else
            {
                node->key_hold_trigger_state &= ~KEY_HOLD_STATE_0_TRID;
            }
#ifndef KEY_CHECK_DISABLE_TIME_HOLD_1
            if(node->key_hold_time_counter >= node->key_hold_time_check[1])
            {
                if((node->key_trigger_type & KEY_TRIGGER_HOLD_1)&&(!(node->key_hold_trigger_state & KEY_HOLD_STATE_1_TRID)))
                {
                    key_send_message(node, KEY_TRIGGER_HOLD_1 + node->combo_count);
                    node->key_hold_trigger_state |= KEY_HOLD_STATE_1_TRID;
                }
            }
            else
            {
                node->key_hold_trigger_state &= ~KEY_HOLD_STATE_1_TRID;
            }
#endif
#ifndef KEY_CHECK_DISABLE_TIME_HOLD_2
            if(node->key_hold_time_counter >= node->key_hold_time_check[2])
            {
                if((node->key_trigger_type & KEY_TRIGGER_HOLD_2)&&(!(node->key_hold_trigger_state & KEY_HOLD_STATE_2_TRID)))
                {
                    key_send_message(node, KEY_TRIGGER_HOLD_2 + node->combo_count);
                    node->key_hold_trigger_state |= KEY_HOLD_STATE_2_TRID;
                }
            }
            else
            {
                node->key_hold_trigger_state &= ~KEY_HOLD_STATE_2_TRID;
            }
#endif
#ifndef KEY_CHECK_DISABLE_TIME_HOLD_3
            if(node->key_hold_time_counter >= node->key_hold_time_check[3])
            {
                if((node->key_trigger_type & KEY_TRIGGER_HOLD_3)&&(!(node->key_hold_trigger_state & KEY_HOLD_STATE_3_TRID)))
                {
                    key_send_message(node, KEY_TRIGGER_HOLD_3 + node->combo_count);
                    node->key_hold_trigger_state |= KEY_HOLD_STATE_3_TRID;
                }
            }
            else
            {
                node->key_hold_trigger_state &= ~KEY_HOLD_STATE_3_TRID;
            }
#endif
#endif
        }
        // Release Check
        else
        {
            node->key_hold_time_counter = 0;
            node->key_hold_trigger_state &= ~KEY_HOLD_STATE_PRESSED;
            if(((node->key_hold_trigger_state & KEY_HOLD_STATE_RELEASED) == 0) && (node->key_trigger_type & KEY_TRIGGER_RELEASED))
            {
                key_send_message(node, KEY_TRIGGER_RELEASED);
                node->key_hold_trigger_state |= KEY_HOLD_STATE_RELEASED;
            }
            if((node->combo_timeout & 0xf0) != 0x00)
            {
                node->combo_timeout -= 0x10;
                if((node->combo_timeout & 0xf0) == 0x00)
                {
                    if((node->key_hold_trigger_state & KEY_HOLD_STATE_ANY_LONG_TRID) == 0)
                    {
                        if((node->combo_count == 1) && ((node->key_trigger_type & KEY_TRIGGER_CLICK)))
                        {
                            // key_send_message(node,KEY_TRIGGER_CLICK);
                            TriggerResult = KEY_TRIGGER_CLICK;
                        }
                        else if((node->combo_count == 2) && ((node->key_trigger_type & KEY_TRIGGER_DOUBLE_CLICK)))
                        {
                            TriggerResult = KEY_TRIGGER_DOUBLE_CLICK;
                        }
                        else if((node->combo_count == 3) && ((node->key_trigger_type & KEY_TRIGGER_TRIPLE_CLICK)))
                        {
                            TriggerResult = KEY_TRIGGER_TRIPLE_CLICK;
                        }
                        else if((node->combo_count == 4) && ((node->key_trigger_type & KEY_TRIGGER_QUADRA_CLICK)))
                        {
                            TriggerResult = KEY_TRIGGER_QUADRA_CLICK;
                        }
                        else
                        {
                            TriggerResult = KEY_TRIGGER_UNSUPPORT;
                        }
                        if(TriggerResult != KEY_TRIGGER_UNSUPPORT)
                        {
                            key_send_message(node,TriggerResult);
                        }
                    }
                    node->combo_count = 0;
                }
                // log_info("timeout etc:%d\n", node->combo_timeout >> 4);
            }
        }
    }
}

#define KEY_CHECK_ERR_HADE_NULL_PTR -1
#define KEY_CHECK_ERR_Ok 0

// struct list_head key_check_member_list;

// 如果设置多个扫描器
#ifdef SKEY_USE_MULTI_SCANNER
inline static int key_check_add(key_check_member_t *member, struct list_head *key_check_member_list)
#else
static struct list_head skey_check_member_list;
#define key_check_member_list (&skey_check_member_list)
inline static int key_check_add(key_check_member_t *member)
#endif
{
    int err;
    // log_info("%s\n",__FUNCTION__);
    if((member->portInitCallback == NULL) || (member->getPortValueCallback == NULL) || (member->trigger_callback == NULL))
    {
        return KEY_CHECK_ERR_HADE_NULL_PTR;
    }
    err = member->portInitCallback(member->port_info);
    ASSERT(err == 0);
    member->key_last_state = member->getPortValueCallback(member->port_info);
    member->key_last_state_temp = member->key_last_state;
    member->combo_count = 0;
    if(member->key_last_state == KEY_STATE_PRESSED){
        member->key_hold_trigger_state = KEY_HOLD_STATE_PRESSED;
    }
    else{
        member->key_hold_trigger_state =  KEY_HOLD_STATE_RELEASED;
    }
    member->key_hold_time_counter = 0;
    INIT_LIST_HEAD(&(member->list));
    list_add_tail(&(member->list), key_check_member_list);
    return KEY_CHECK_ERR_Ok;
}

extern const key_check_member_t key_check_member_begin[];
extern const key_check_member_t key_check_member_end[];
#ifdef SKEY_USE_MULTI_SCANNER
#define do_KEY_CHECK_REGISTER(p,key_list) \
for(p=key_check_member_begin;p<key_check_member_end;p++){key_check_add(p,key_list);}
#else
#define do_KEY_CHECK_REGISTER(p) \
for(p=key_check_member_begin;p<key_check_member_end;p++){key_check_add(p);}
#endif


#ifdef SKEY_USE_MULTI_SCANNER
void key_check_init(struct list_head * key_check_member_list,u32 scan_period)
#else
void key_check_init(void)
#endif
{
    key_check_member_t *node;
    INIT_LIST_HEAD(key_check_member_list);
#ifdef SKEY_USE_MULTI_SCANNER
    do_KEY_CHECK_REGISTER(node,key_check_member_list);
#else
    do_KEY_CHECK_REGISTER(node);
#endif
    usr_timer_add(key_check_member_list,&key_check_handler, 
#ifdef SKEY_USE_MULTI_SCANNER
    scan_period,
#else
    30,
#endif
    0);
}
// void key_msg_handler(int* sender, keyTriggerTypeDef signal)
void key_msg_handler(int* msg)
{
    key_check_member_t *key_member = (key_check_member_t*)msg[0];
    if(key_member->trigger_callback)
    {
        key_member->trigger_callback(key_member,(keyTriggerTypeDef)msg[1]);
    }
}

APP_MSG_HANDLER(_key_msg_handler) = 
{
    .owner = 0xff,
    .from  = MSG_FROM_SELF_KEY,
    .handler = key_msg_handler,
};




/*****************example code: **************************/
#define ACTIVATE_KEY_DEMO_CODE
#ifdef ACTIVATE_KEY_DEMO_CODE
#define GPIO_INIT_ERR_PORT_OK 0
#define GPIO_INIT_ERR_PORT_INVALID -1
#define GPIO_INIT_ERR_BIT_INVALID -2
#define GPIO_INIT_ERR_NO_DEVICE_DEFINE -3
#if  defined(DEVICE_BR50)
#include "gpio_config.h"

uint8_t io_num_check[6] = {7,5,8,10,0,6};

int getPortValue(void *port_info)
{
    uint32_t bit  = (uint32_t)port_info & 0x000f;
    uint32_t port = (uint32_t)port_info >> 4;;
    int i = bit;
    for(bit = 0x01; i>0; i--)
    {
        bit <<= 1;
    }
    // log_info("%s\n", __FUNCTION__);
    // log_info("read port:%d,pin:%d", port, bit);
    return gpio_read((u32)port_info);
    // return gpio_read_port(port, bit);
}

int portInit(void *port_info)
{
    uint32_t bit  = (uint32_t)port_info & 0x000f;
    uint32_t port = (uint32_t)port_info >> 4;;
    int i;
    struct gpio_config port_config = 
    {
        .mode = PORT_INPUT_PULLUP_10K,
        .hd = PORT_DRIVE_STRENGT_24p0mA
    };
    if(port > 5)
    {
        return GPIO_INIT_ERR_PORT_INVALID;
    }
    if((bit+1) > io_num_check[port])
    {
        return GPIO_INIT_ERR_BIT_INVALID;
    }
    i = bit;
    for(bit = 0x01; i>0; i--)
    {
        bit <<= 1;
    }
    port_config.pin = bit;
    gpio_deinit((enum gpio_port)port, bit);
    log_info("init port:%d,pin:%d", port, bit);
    gpio_init((enum gpio_port)port, &port_config);
    return GPIO_INIT_ERR_PORT_OK;
}
#else
int getPortValue(void *port_info)
{
    return GPIO_INIT_ERR_NO_DEVICE_DEFINE;
}
int portInit(void *port_info)
{
    return GPIO_INIT_ERR_NO_DEVICE_DEFINE;
}
#endif

#endif